#include "LuaGlue.h"

#include "App.h"
#include "core/Object.h"


void do_lua_func0(int idx)
{
	FrameTimes& fs = App::getSingleton().getFrameStats();
	unsigned long now = App::getSingleton().getTimer()->getMilliseconds();
	lua_State* L = App::getSingleton().getLuaState();
	lua_rawgeti(L, LUA_REGISTRYINDEX, idx);
	lua_pcall(L,0,0,0);
	int dt = App::getSingleton().getTimer()->getMilliseconds() - now;
	fs.script += dt;
	return;
}

template<typename T>
void do_lua_func1(int idx, T v)
{
	lua_State* L = App::getSingleton().getLuaState();
	lua_rawgeti(L, LUA_REGISTRYINDEX, idx);
	lua_push(L, T, v);
	lua_pcall(L,1,0,0);
	return;
}

template<typename A, typename B>
void do_lua_func2(int idx, A a, B b)
{
	lua_State* L = App::getSingleton().getLuaState();
	lua_rawgeti(L, LUA_REGISTRYINDEX, idx);
	lua_push(L, A, a);
	lua_push(L, B, b);
	lua_pcall(L,2,0,0);
	return;
}

//#define lua_fun(idx, nargs, ...)

void do_lua_func(int idx)
{
	lua_State* L = App::getSingleton().getLuaState();
	lua_pushvalue(L, -2);
	lua_rawgeti(L, LUA_REGISTRYINDEX, idx);
	lua_pcall(L,1,0,0);
	return;
}

int loadSceneXml(lua_State* L)
{
	const char* file = luaL_checkstring(L, 1);
	App::getSingletonPtr()->loadSceneXml(file);
	return 1;
}

int getTime(lua_State* L)
{
	float time = .001f * App::getSingletonPtr()->getTimer()->getMilliseconds();
	lua_pushnumber(L, time);
	return 1;
}

int getFrameTime(lua_State* L)
{
	float dt = .001f * App::getSingletonPtr()->getFrameTime();
	lua_pushnumber(L, dt);
	return 1;
}



namespace LuaGlue
{
	class EventManager
	{
	public:
		static const char className[];
		static const luaL_reg meta[];
		static const luaL_reg methods[];

		static int create(lua_State *L)
		{
			return 1;
		}

		static int registerCallback(lua_State *L);
	};
}

int LuaGlue::EventManager::registerCallback(lua_State *L)
{
//	if(!lua_isfunction(L, -1)) return 0;
	int f = luaL_ref(L, LUA_REGISTRYINDEX);
	if(f == LUA_REFNIL) return 0;

	::GameObject* go = unwrap< ::GameObject>(L, 1);
	std::string eventName = luaL_checkstring(L, 2);

//	std::cout << "[EventManager::registerCallback] Binding " << go->getName() << ":" << eventName << " to " << f << std::endl;

	::App& app = ::App::getSingleton();
	if(eventName == "update")
		{
			app.onFrameStarted.connect( sigc::bind( sigc::ptr_fun(do_lua_func1<std::string>),  f, go->getName()) );
			return 1;
		}

//	if(strcmp(eventName, "keydown")==0)
//		{
//		app.onKeyDown.connect( sigc::bind( sigc::ptr_fun(do_lua_func2<std::string, int>),  f, go->getName(), 0) );
	if(!go->hasProperty("actions")) return 1;
	IProperty* p = go->getIProperty("actions");
	::Object* ao = static_cast< ::Object*>(p->_getValuePtr());
	ao->addProperty<int>(eventName, f);
//		}

	return 1;
}

DECLARE(EventManager)
META(EventManager)
METHODS(EventManager)
method(LuaGlue::EventManager, registerCallback),
       END

       namespace LuaGlue
{
	void Register(lua_State* L)
	{
		assert(L);

//		Wrapper<Degree>::Register(L);
//		Wrapper<Radian>::Register(L);
//		Wrapper<ColourValue>::Register(L);
		Wrapper<Vector3>::Register(L);
		Wrapper<Quaternion>::Register(L);
		Wrapper<Camera>::Register(L);
//		Wrapper<Overlay>::Register(L);

//		Wrapper<RigidBody>::Register(L);

		Wrapper<Object>::Register(L);
		Wrapper<GameObject>::Register(L);
		Wrapper<App>::Register(L);
		Wrapper<EventManager>::Register(L);
		Wrapper<Timer>::Register(L);

		lua_register( L, "getByName", &LuaGlue::GameObject::getByName );
		lua_register( L, "load", &loadSceneXml );
		lua_register( L, "time", &getTime);
		lua_register( L, "frameTime", &getFrameTime);
	}
}

int LuaFunctionHelper::Execute(lua_State* L)
{
	std::cout << "LuaFunctionHelper::Execute" << std::endl;

	int nargs = lua_gettop(L);
	std::cout << "nargs: " << nargs << std::endl;

	/*
			f.args = new ValueArray();
			ValueArray* args = f.args;

			for(int i=1; i<=nargs; i++)
				{
					const char* typeName = luaL_typename(L, i);
					std::cout << i << ")" << "[" << typeName << "]" << std::endl;
					switch(lua_type(L,i))
						{
						case LUA_TBOOLEAN:
	//						args->push(::Value((bool)luaL_checknumber(L,i)));
							break;
						case LUA_TNUMBER:
	//						args->push(::Value((float)luaL_checknumber(L,i)));
							break;
						case LUA_TSTRING:
	//						args->push(::Value(luaL_checkstring(L,i)));
							break;
						case LUA_TTABLE:
							//!TODO:
							break;
						default:
							break;
						}
				}
	*/
	
	lua_pushnil(L);
	try
		{
			f();
//			::Value v = f();
//			std::cout << v << std::endl;
		}
	catch(std::exception& e)
		{
			std::cout << e.what() << std::endl;
		}
	catch(...)
		{
			std::cout << "unhandled exception..." << std::endl;
		}

	return 1;
}
